
package com.versiFramework.controller.utils
{
	import com.versiFramework.versi;
	import com.versiFramework.core.IService;
	import com.versiFramework.core.serviceCommand;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;

	public class chainServices
	{
		private var _commands:Array= new Array;
		private var runCommand:int=0;
		private var sync:Boolean=true;
		private var handler:Function;
		private var faultHandler:Function;
		private var silent:Boolean = true;
		private var finished:Boolean = false;
		//* constructor *//
		public function chainServices(onComplete:Function,synchronous:Boolean=true,silentErrors:Boolean=true,onFault:Function=null)
		{
			sync = synchronous;
			handler = onComplete;
			silent = silentErrors;
			faultHandler = onFault;
		}

		
		public function addCommand(serviceFunction:String,arguments:Object=null,resultHandler:Function=null):void
		{
			var newCommand:serviceCommand = new serviceCommand;
			newCommand.service = serviceFunction.slice(0,serviceFunction.indexOf("."));
			newCommand.remoteFunction = serviceFunction.slice(serviceFunction.indexOf(".")+1,serviceFunction.length);
			newCommand.arguments = arguments;
			newCommand.resultHandler = resultHandler;
			
			
			if (versi.getClass(newCommand.service) && versi.getClass(newCommand.service) is IService)
			{
				newCommand.commandID = _commands.length;
				_commands.push(newCommand);
			}
			else
			{
				throw new Error ( "No service instance was found with the name " + newCommand.service );
				clearCommandStructure();
			}
		}
		
		public function start():void
		{
			if (Boolean(handler) && _commands.length)
			{
				if (sync)
				{
					doCommand(_commands[runCommand])
				}
				else
				{
					for each (var command:serviceCommand in _commands)
					{
						doCommand(command)
					}
				}
			}
		}
		
		private function doCommand(command:serviceCommand):void
		{
			var service:IService = versi.getClass(command.service)
				service.send(command.remoteFunction,command.arguments,false,commandResult,commandFault,command)
		}		
		
		private function commandResult(evt:ResultEvent):void
		{
			var command:serviceCommand = evt.token.tokenProperty;
			var result:String;
			if (Boolean(command.resultHandler))
			{
				result = command.resultHandler.call(null,evt);
				//if we are running syncronously, allow breaking out of command channing 
				if (result && result=='false' && sync == true)
				{
					clearCommandStructure();
				}
				else 
				{
					command.commandComplete = true
					if (sync == true)
					{
						//next command or finish	
						++runCommand;
						if (runCommand < _commands.length)
						{
							doCommand(_commands[runCommand])
						}
						else
						{
							finish()
						}
					}
					else
					{
						//test if all commands have completed
						if (isComplete()) finish();
					}
				}
			}
		}
		
		private function commandFault(evt:FaultEvent):void
		{
			if (silent == false && Boolean(faultHandler))
			{
				faultHandler.call(null,evt);
			}
			else if (silent == false)
			{
				var command:serviceCommand = evt.token.tokenProperty;
				var service:IService = versi.getClass(command.service);
				service.doServiceFault(evt.message);
			}
			handler.call(null,false);
			clearCommandStructure()
		}

		
		private function isComplete():Boolean
		{
		    var iscomplete:Boolean = true;
			for each (var command:serviceCommand in _commands)
			{
				if (command.commandComplete == false)
				{
					iscomplete = false;
					break;
				}
			}
			return iscomplete;
		}
		
		private function finish():void
		{
			if (!this.finished)
			{ 
				this.finished=true;
				handler.call(null,true);
				clearCommandStructure();
			}
		}
				
		private function clearCommandStructure():void
		{
			while (_commands.length)
			{
				_commands[0].resultHandler = null;
				_commands[0].arguments = null;
				_commands.shift();
			}
			this.faultHandler = null;
			this.handler = null;
			delete this;
		}

		
	}
}